#include <iostream>
#include <future>
#include <memory>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
using namespace std;
class ThreadPool
{
    using Functor = function<void()>;

public:
    ThreadPool(int count = 1) : _stop(false)
    {
        for (int i = 0; i < count; i++)
        {
            _threads.emplace_back(&ThreadPool::entry, this);
        }
    }
    ~ThreadPool()
    {
        stop();
    }
    void stop()
    {
        if (_stop == true)
            return;
        _stop = true;
        _cond.notify_all();
        for (int i = 0; i < _threads.size(); ++i)
            _threads[i].join();
    }
    template <typename F, typename... Args>
    auto push(F &&func, Args &&...args) -> future<decltype(func(args...))>
    {
        using return_type = decltype(func(args...));
        auto function = bind(forward<F>(func), forward<Args>(args)...);
        auto task = make_shared<packaged_task<return_type()>>(function);
        future<return_type> fut = task->get_future();
        {
            unique_lock<mutex> lock(_mutex);
            _tasks.push_back([task]()
                             { (*task)(); });
            _cond.notify_one();
        }
        return fut;
    }

private:
    void entry()
    {
        while (1)
        {
            if (_stop == true)
                return;
            vector<Functor> tasks;
            {
                unique_lock<mutex> lock(_mutex);
                _cond.wait(lock, [this]()
                           { return _stop == true || _tasks.empty() == false; });
                _tasks.swap(tasks);
            }
            for (auto &task : tasks)
                task();
        }
    }

private:
    bool _stop;
    mutex _mutex;
    condition_variable _cond;
    vector<thread> _threads;
    vector<Functor> _tasks;
};
int Add(int num1, int num2)
{
    return num1 + num2;
}
int main()
{
    ThreadPool pool;
    for (int i = 0; i < 10; ++i)
    {
        future<int> fut = pool.push(Add, 1, i);
        cout << fut.get() << endl;
    }
    pool.stop();
    return 0;
}